"""视频捕获封装模块"""

import cv2
import threading
import time
from typing import Optional, Dict, Any
import numpy as np
import logging
from datetime import datetime
from config import get_config

# 配置日志
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger('video_capture')

class VideoCapture:
    """视频捕获封装类，支持RTSP流、本地摄像头和视频文件"""
    
    def __init__(self, source: Any = 0, name: str = 'default'):
        """初始化视频捕获
        
        Args:
            source: 视频源，可以是摄像头ID、视频文件路径或RTSP URL
            name: 视频源名称
        """
        try:
            self.source = source
            self.name = name
            self.cap = None
            self.frame = None
            self.running = False
            self.thread = None
            self.lock = threading.Lock()
            self.last_frame_time = 0
            self.frame_count = 0
            self.fps = 0
            self.resolution = (0, 0)
            
            # 配置参数
            self.buffer_size = get_config('video.buffer_size', 1)
            self.reconnect_interval = get_config('video.reconnect_interval', 5.0)
            self.max_reconnect_attempts = get_config('video.max_reconnect_attempts', 5)
            self.frame_timeout = get_config('video.frame_timeout', 10.0)
            
            # 状态标志
            self.is_connected = False
            self.reconnect_attempts = 0
            self.last_reconnect_time = 0
            
            logger.info(f"视频捕获器已初始化: {self.name}, 源: {self.source}")
            logger.debug(f"初始化参数 - 缓冲区大小: {self.buffer_size}, 重连间隔: {self.reconnect_interval}s, "
                         f"最大重连尝试: {self.max_reconnect_attempts}, 帧超时: {self.frame_timeout}s")
        except Exception as e:
            logger.error(f"初始化视频捕获器失败: {e}", exc_info=True)
            # 确保所有属性都被初始化
            self.source = source
            self.name = name
            self.cap = None
            self.frame = None
            self.running = False
    
    def start(self) -> bool:
        """启动视频捕获
        
        Returns:
            是否启动成功
        """
        try:
            logger.info(f"正在启动视频捕获: {self.name}")
            
            # 检查是否已经在运行
            if self.running:
                logger.warning(f"视频捕获已在运行中: {self.name}")
                return True
            
            # 初始化视频捕获
            if not self._initialize_capture():
                logger.error(f"初始化视频捕获失败: {self.name}")
                return False
            
            # 启动捕获线程
            self.running = True
            self.thread = threading.Thread(target=self._capture_loop, daemon=True, name=f"CaptureThread_{self.name}")
            self.thread.start()
            
            # 等待第一帧
            start_time = time.time()
            frame_wait_timeout = 5.0
            logger.debug(f"等待第一帧，超时时间: {frame_wait_timeout}s")
            
            while self.frame is None and time.time() - start_time < frame_wait_timeout:
                time.sleep(0.1)
            
            if self.frame is not None:
                logger.info(f"视频捕获已成功启动: {self.name}, 分辨率: {self.resolution}, FPS: {self.fps:.2f}")
                return True
            else:
                logger.error(f"无法获取视频帧，启动超时: {self.name}")
                self.stop()
                return False
                
        except Exception as e:
            logger.error(f"启动视频捕获失败: {self.name}, 错误: {e}", exc_info=True)
            self.stop()
            return False
    
    def _initialize_capture(self) -> bool:
        """初始化OpenCV视频捕获
        
        Returns:
            是否初始化成功
        """
        try:
            logger.info(f"初始化视频捕获: {self.name}, 源: {self.source}")
            
            # 验证源是否有效
            if self.source is None:
                logger.error(f"无效的视频源: None")
                return False
            
            # 创建视频捕获对象
            logger.debug(f"创建视频捕获对象: {self.source}")
            self.cap = cv2.VideoCapture(self.source)
            
            # 检查捕获对象是否创建成功
            if self.cap is None:
                logger.error(f"无法创建视频捕获对象: {self.source}")
                return False
            
            # 设置缓冲区大小（针对网络流）
            if isinstance(self.source, str):
                if self.source.startswith(('rtsp://', 'http://', 'https://')):
                    logger.debug(f"为网络流设置缓冲区大小: {self.buffer_size}")
                    try:
                        self.cap.set(cv2.CAP_PROP_BUFFERSIZE, self.buffer_size)
                    except Exception as e:
                        logger.warning(f"设置缓冲区大小时出错: {e}")
            
            # 检查是否成功打开
            if not self.cap.isOpened():
                logger.error(f"无法打开视频源: {self.source}")
                if self.cap:
                    self.cap.release()
                    self.cap = None
                return False
            
            # 获取视频属性
            try:
                self.fps = max(1.0, self.cap.get(cv2.CAP_PROP_FPS))  # 确保FPS至少为1.0
                width = max(1, int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)))
                height = max(1, int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
                self.resolution = (width, height)
            except Exception as e:
                logger.warning(f"获取视频属性时出错: {e}")
                # 使用默认值
                self.fps = 30.0
                self.resolution = (640, 480)
            
            logger.info(f"成功打开视频源: {self.name}, 分辨率: {self.resolution}, FPS: {self.fps:.2f}")
            
            # 重置重连计数
            self.is_connected = True
            self.reconnect_attempts = 0
            
            return True
            
        except Exception as e:
            logger.error(f"初始化视频捕获失败: {self.name}, 错误: {e}", exc_info=True)
            self.is_connected = False
            if self.cap:
                try:
                    self.cap.release()
                except Exception as release_error:
                    logger.warning(f"释放视频捕获对象时出错: {release_error}")
                self.cap = None
            return False
    
    def _capture_loop(self) -> None:
        """视频捕获循环线程"""
        logger.info(f"开始视频捕获循环: {self.name}")
        
        # 用于FPS计算的变量
        last_fps_calc_time = time.time()
        frames_since_last_calc = 0
        
        while self.running:
            try:
                # 检查连接状态
                if not self.is_connected or self.cap is None:
                    logger.debug(f"连接丢失，尝试重连: {self.name}")
                    self._reconnect()
                    time.sleep(0.1)  # 避免CPU占用过高
                    continue
                
                # 读取帧
                ret, frame = self.cap.read()
                
                if not ret:
                    logger.warning(f"读取帧失败: {self.name}")
                    self.is_connected = False
                    continue
                
                # 验证帧是否有效
                if frame is None or frame.size == 0:
                    logger.warning(f"获取到无效帧: {self.name}")
                    self.is_connected = False
                    continue
                
                # 更新帧和时间戳
                with self.lock:
                    try:
                        self.frame = frame.copy()
                        current_time = time.time()
                        self.last_frame_time = current_time
                        self.frame_count += 1
                        frames_since_last_calc += 1
                    except Exception as copy_error:
                        logger.error(f"复制帧数据时出错: {copy_error}")
                        continue
                
                # 计算实际FPS (每30帧或每秒计算一次)
                current_time = time.time()
                if frames_since_last_calc >= 30 or (current_time - last_fps_calc_time) >= 1.0:
                    time_diff = current_time - last_fps_calc_time
                    if time_diff > 0:  # 确保时间差大于零，避免除零错误
                        new_fps = frames_since_last_calc / time_diff
                        # 平滑FPS变化
                        self.fps = 0.7 * self.fps + 0.3 * new_fps
                    
                    # 重置计数器
                    last_fps_calc_time = current_time
                    frames_since_last_calc = 0
                    
                    # 定期记录状态
                    if self.frame_count % 300 == 0:  # 大约每10秒记录一次
                        logger.debug(f"捕获状态: {self.name}, FPS: {self.fps:.2f}, 总帧数: {self.frame_count}")
                
                # 重置重连计数
                self.reconnect_attempts = 0
                
            except Exception as e:
                logger.error(f"捕获循环异常: {self.name}, 错误: {e}", exc_info=True)
                self.is_connected = False
                time.sleep(0.1)  # 避免异常时CPU占用过高
        
        logger.info(f"视频捕获循环已停止: {self.name}")
    
    def _reconnect(self) -> None:
        """重新连接视频源"""
        current_time = time.time()
        
        # 检查重连间隔
        if current_time - self.last_reconnect_time < self.reconnect_interval:
            return
        
        self.last_reconnect_time = current_time
        
        # 检查最大重连次数
        if self.reconnect_attempts >= self.max_reconnect_attempts:
            logger.error(f"已达到最大重连次数: {self.max_reconnect_attempts}, 停止重连: {self.name}")
            return
        
        self.reconnect_attempts += 1
        logger.warning(f"正在重连视频源... (尝试 {self.reconnect_attempts}/{self.max_reconnect_attempts}): {self.name}")
        
        # 释放旧的捕获对象
        if self.cap:
            try:
                logger.debug(f"释放旧的视频捕获对象: {self.name}")
                self.cap.release()
            except Exception as release_error:
                logger.warning(f"释放视频捕获对象时出错: {release_error}")
            finally:
                self.cap = None
        
        # 重新初始化
        logger.debug(f"开始重新初始化视频捕获: {self.name}")
        success = self._initialize_capture()
        
        if success:
            logger.info(f"视频源重连成功: {self.name}")
        else:
            logger.error(f"视频源重连失败: {self.name}, 尝试次数: {self.reconnect_attempts}/{self.max_reconnect_attempts}")
    
    def read(self) -> Optional[np.ndarray]:
        """读取当前帧
        
        Returns:
            当前帧，如果没有可用帧则返回None
        """
        try:
            with self.lock:
                # 检查帧是否有效
                if self.frame is None:
                    logger.debug(f"没有可用帧: {self.name}")
                    return None
                
                # 检查帧是否超时
                current_time = time.time()
                frame_age = current_time - self.last_frame_time
                
                if frame_age >= self.frame_timeout:
                    logger.warning(f"帧已超时: {self.name}, 帧年龄: {frame_age:.2f}s, 超时阈值: {self.frame_timeout}s")
                    return None
                
                # 验证帧数据有效性
                if self.frame.size == 0 or len(self.frame.shape) < 2:
                    logger.warning(f"无效的帧数据: {self.name}, 形状: {self.frame.shape if hasattr(self.frame, 'shape') else '未知'}")
                    return None
                
                # 复制并返回帧
                try:
                    frame_copy = self.frame.copy()
                    # 记录读取帧的统计信息（每100帧记录一次）
                    if self.frame_count % 100 == 0:
                        logger.debug(f"读取帧: {self.name}, 形状: {frame_copy.shape}, 帧年龄: {frame_age:.2f}s")
                    return frame_copy
                except Exception as copy_error:
                    logger.error(f"复制帧时出错: {self.name}, 错误: {copy_error}")
                    return None
        except Exception as e:
            logger.error(f"读取帧时发生异常: {self.name}, 错误: {e}", exc_info=True)
            return None
    
    def stop(self) -> None:
        """停止视频捕获"""
        logger.info(f"正在停止视频捕获: {self.name}")
        
        # 设置停止标志
        self.running = False
        
        # 等待线程结束
        if self.thread and self.thread.is_alive():
            try:
                logger.debug(f"等待捕获线程结束: {self.name}")
                self.thread.join(timeout=3.0)  # 增加超时时间以确保线程有足够时间清理
                if self.thread.is_alive():
                    logger.warning(f"捕获线程在超时后仍在运行: {self.name}")
            except Exception as join_error:
                logger.error(f"等待线程结束时出错: {join_error}")
            finally:
                self.thread = None
        
        # 释放捕获对象
        if self.cap:
            try:
                logger.debug(f"释放视频捕获对象: {self.name}")
                self.cap.release()
            except Exception as release_error:
                logger.error(f"释放视频捕获对象时出错: {release_error}")
            finally:
                self.cap = None
        
        # 重置状态
        self.is_connected = False
        try:
            self.frame = None
        except Exception as clear_error:
            logger.warning(f"清除帧数据时出错: {clear_error}")
        
        logger.info(f"视频捕获已成功停止: {self.name}, 总捕获帧数: {self.frame_count}")
    
    def get_status(self) -> Dict[str, Any]:
        """获取捕获器状态
        
        Returns:
            状态信息字典
        """
        try:
            status = {
                'name': self.name,
                'source': self.source,
                'connected': self.is_connected,
                'running': self.running,
                'fps': round(self.fps, 2),
                'resolution': self.resolution,
                'frame_count': self.frame_count,
                'reconnect_attempts': self.reconnect_attempts,
                'uptime': None,  # 可以在需要时添加运行时间计算
                'last_frame_time': self.last_frame_time
            }
            
            # 计算帧年龄
            if self.last_frame_time > 0:
                frame_age = time.time() - self.last_frame_time
                status['frame_age'] = round(frame_age, 2)
            
            logger.debug(f"获取状态: {self.name}, 连接: {self.is_connected}, FPS: {self.fps:.2f}")
            return status
        except Exception as e:
            logger.error(f"获取状态时出错: {self.name}, 错误: {e}")
            # 返回基本状态，确保即使出错也能提供一些信息
            return {
                'name': getattr(self, 'name', 'unknown'),
                'connected': False,
                'running': False,
                'error': str(e)
            }
    
    def set_property(self, prop_id: int, value: float) -> bool:
        """设置视频属性
        
        Args:
            prop_id: 属性ID (OpenCV CAP_PROP_*)
            value: 属性值
            
        Returns:
            是否设置成功
        """
        try:
            # 验证参数
            if not isinstance(prop_id, int):
                logger.warning(f"无效的属性ID类型: {type(prop_id)}")
                return False
            
            if not isinstance(value, (int, float)):
                logger.warning(f"无效的属性值类型: {type(value)}")
                return False
            
            # 检查捕获器状态
            if not self.cap or not self.is_connected:
                logger.warning(f"无法设置属性，捕获器未连接: {self.name}")
                return False
            
            # 执行属性设置
            result = self.cap.set(prop_id, value)
            
            # 记录属性设置
            props_names = self.get_opencv_props()
            prop_name = next((name for name, id in props_names.items() if id == prop_id), f"未知(ID:{prop_id})")
            
            if result:
                logger.debug(f"成功设置属性: {self.name}, {prop_name} = {value}")
            else:
                logger.warning(f"无法设置属性: {self.name}, {prop_name} = {value}")
            
            return result
        except Exception as e:
            logger.error(f"设置属性时出错: {self.name}, 错误: {e}")
            return False
    
    def get_property(self, prop_id: int) -> float:
        """获取视频属性
        
        Args:
            prop_id: 属性ID (OpenCV CAP_PROP_*)
            
        Returns:
            属性值
        """
        try:
            # 验证参数
            if not isinstance(prop_id, int):
                logger.warning(f"无效的属性ID类型: {type(prop_id)}")
                return 0.0
            
            # 检查捕获器状态
            if not self.cap or not self.is_connected:
                logger.debug(f"无法获取属性，捕获器未连接: {self.name}")
                return 0.0
            
            # 执行属性获取
            value = self.cap.get(prop_id)
            
            # 记录属性获取
            props_names = self.get_opencv_props()
            prop_name = next((name for name, id in props_names.items() if id == prop_id), f"未知(ID:{prop_id})")
            logger.debug(f"获取属性: {self.name}, {prop_name} = {value}")
            
            return value
        except Exception as e:
            logger.error(f"获取属性时出错: {self.name}, 错误: {e}")
            return 0.0
    
    def is_opened(self) -> bool:
        """检查视频源是否已打开
        
        Returns:
            是否已打开
        """
        try:
            # 执行额外的检查
            is_open = self.is_connected and self.cap is not None
            
            # 对于已连接的捕获器，尝试执行一个简单的操作以验证其有效性
            if is_open:
                try:
                    # 检查捕获对象是否仍然有效
                    is_open = self.cap.isOpened()
                except Exception as check_error:
                    logger.warning(f"验证捕获器状态时出错: {check_error}")
                    is_open = False
            
            return is_open
        except Exception as e:
            logger.error(f"检查捕获器状态时出错: {self.name}, 错误: {e}")
            return False
    
    def __del__(self):
        """析构函数，确保资源被释放"""
        try:
            # 避免在程序退出时可能发生的日志记录问题
            if hasattr(self, 'name'):
                logger.debug(f"析构函数调用: {self.name}")
            self.stop()
        except Exception:
            # 在析构函数中避免抛出异常
            pass
    
    # 静态方法：获取常用的OpenCV属性ID
    @staticmethod
    def get_opencv_props() -> Dict[str, int]:
        """获取常用的OpenCV视频属性ID
        
        Returns:
            属性名称到ID的映射
        """
        try:
            # 创建属性映射，添加错误处理以防止属性不存在
            props = {}
            
            # 使用try-except确保即使某些属性在特定的OpenCV版本中不存在也不会崩溃
            try:
                props['fps'] = cv2.CAP_PROP_FPS
            except AttributeError:
                logger.warning("OpenCV CAP_PROP_FPS 属性不存在")
            
            try:
                props['width'] = cv2.CAP_PROP_FRAME_WIDTH
                props['height'] = cv2.CAP_PROP_FRAME_HEIGHT
            except AttributeError as e:
                logger.warning(f"OpenCV 分辨率属性不存在: {e}")
            
            # 图像质量相关属性
            for name, attr in [
                ('brightness', 'CAP_PROP_BRIGHTNESS'),
                ('contrast', 'CAP_PROP_CONTRAST'),
                ('saturation', 'CAP_PROP_SATURATION'),
                ('hue', 'CAP_PROP_HUE'),
                ('gain', 'CAP_PROP_GAIN'),
                ('exposure', 'CAP_PROP_EXPOSURE'),
                ('buffer_size', 'CAP_PROP_BUFFERSIZE')
            ]:
                try:
                    props[name] = getattr(cv2, attr)
                except AttributeError:
                    logger.debug(f"OpenCV {attr} 属性不存在")
            
            logger.debug(f"获取OpenCV属性映射，成功加载 {len(props)} 个属性")
            return props
        except Exception as e:
            logger.error(f"获取OpenCV属性时出错: {e}")
            # 返回基础属性集，确保即使出错也能提供核心功能
            return {
                'fps': getattr(cv2, 'CAP_PROP_FPS', 5),
                'width': getattr(cv2, 'CAP_PROP_FRAME_WIDTH', 3),
                'height': getattr(cv2, 'CAP_PROP_FRAME_HEIGHT', 4)
            }